package com.whosly.rapid.lang.thread.hook;

import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;

import com.google.common.collect.MapMaker;
import com.whosly.rapid.lang.thread.executor.MonitorThreadPoolExecutor;
import com.whosly.rapid.lang.thread.factory.INamedThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 线程Hook
 *
 * @author yueny09 <deep_blue_yang@163.com>
 *
 * @DATE 2017年7月20日 上午11:13:41
 *
 */
public class ShutdownHookService {

	private static final Logger logger = LoggerFactory.getLogger(ShutdownHookService.class);

	// 使用WeakValueMap，当Value被垃圾回收时会将此value在map中的entry清除，防止内存溢出
	private static ConcurrentMap<String, ExecutorService> registerExecutorConcurrentMap =
			new MapMaker().weakValues().makeMap();
	private static ConcurrentMap<String, Timer> registerTimerConcurrentMap =
			new MapMaker().weakValues().makeMap();
	private static Set<String> registerNormalSets = new HashSet<>();

	static{
		logger.info("【线程钩子】线程钩子启动...");
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				registerNormalSets.clear();
				registerExecutorConcurrentMap.clear();
				registerTimerConcurrentMap.clear();
			}
		});
	}

	/**
	 * ExecutorService 钩子注册
	 *
	 * @param executorService 包括ScheduledExecutorService
	 */
	public static boolean register(final ExecutorService executorService){
		if(executorService == null){
			return false;
		}

		// 自定义线程池不再注册
		String key;
		if(executorService instanceof MonitorThreadPoolExecutor){
			MonitorThreadPoolExecutor executor = (MonitorThreadPoolExecutor)executorService;

			INamedThreadFactory threadFactory = (INamedThreadFactory)executor.getThreadFactory();
			key = threadFactory.getNamePrefix();
			if(!registerExecutorConcurrentMap.containsKey(key)){
				registerExecutorConcurrentMap.putIfAbsent(key, executorService);
			}else{
				// 已经被注册了
				logger.warn("【非契约精神-警告】线程池前缀: {} 被重复注册!!!", key);
				return false;
			}
		}else{
			registerExecutorConcurrentMap.putIfAbsent(executorService.getClass().toString(), executorService);
		}

		// 钩子线程里只处理善后，目标是尽可能快的退出且不保证有脏数据。如果钩子线程里做过多事情，或者发生阻塞，那么可能出现kill失效，程序不能退出的情况，这是需要强制退出
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				// We'd like to log progress and failures that may arise in
				// the
				// following code, but unfortunately the behavior of logging
				// is undefined in shutdown hooks.
				// This is because the logging code installs a shutdown hook
				// of its
				// own. See Cleaner class inside {@link LogManager}.

				if(!executorService.isShutdown()){
					try {
						executorService.shutdown();
						logger.info("【线程关闭钩子】已对ExecutorService: {} 发送关闭请求.", executorService);
					} catch (Exception e) {
						logger.error("ExecutorService HOOK执行出现异常!", e);
					}
				}
			}
		});
		return true;
	}

	/**
	 * 注册 Timer JVM关闭的的钩子
	 */
	public static boolean register(final Timer timer) {
		if(timer == null) {
			return false;
		}

		registerTimerConcurrentMap.putIfAbsent(timer.getClass().toString(), timer);

		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				if(timer != null){
					try {
						timer.cancel();
						logger.info("【线程关闭钩子】已对Timer: {} 发送关闭请求.", timer);
					} catch (Exception e) {
						logger.error("Timer HOOK执行出现异常!", e);
					}
				}
			}
		});

		return true;
	}

	/**
	 * 自定义线程池行为的 钩子注册
	 *
	 * @param alias 别名
	 * @param target 自定义线程池
	 */
	public static boolean register(String alias, Runnable target){
		if(target == null){
			return false;
		}

		if(registerNormalSets.contains(alias)){
			return false;
		}

		registerNormalSets.add(alias);
		Runtime.getRuntime().addShutdownHook(new Thread(target));

		return true;
	}

	/**
	 * 注册线程池总数
	 */
	public static int size() {
		return registerTimerConcurrentMap.keySet().size() + registerExecutorConcurrentMap.keySet().size()
				+ registerNormalSets.size();
	}

	// public static void register(final IAsynExecter asynExecter) {
	// //
	// 钩子线程里只处理善后，目标是尽可能快的退出且不保证有脏数据。如果钩子线程里做过多事情，或者发生阻塞，那么可能出现kill失效，程序不能退出的情况，这是需要强制退出
	// Runtime.getRuntime().addShutdownHook(new Thread() {
	// @Override
	// public void run() {
	// // We'd like to log progress and failures that may arise in
	// // the
	// // following code, but unfortunately the behavior of logging
	// // is undefined in shutdown hooks.
	// // This is because the logging code installs a shutdown hook
	// // of its
	// // own. See Cleaner class inside {@link LogManager}.
	// asynExecter.shutdown();
	//
	// logger.info("【线程关闭钩子】已对 ExecutorService 发送关闭请求.");
	// }
	// });
	// }

	private ShutdownHookService() {
		// .
	}

}
